﻿#region License

/*
    Copyright (c) 2012 Joseph Haluska
    This source is subject to the Microsoft Public License (Ms-PL).
    Please see http://www.microsoft.com/en-us/openness/licenses.aspx for details.
    All other rights reserved.
*/

#endregion License

using System;
using System.Reflection;
using System.Text;

namespace NetExceptionReporter.Extensions
{
    public static class ExceptionExtensions
    {
        #region Fields

        private const string _tab = "\t";
        private const char _leftBracket = '[';
        private const char _rightBracket = ']';

        #endregion Fields

        #region Members

        public static bool IsEquivalent(this Exception e1, Exception e2)
        {
            while (true)
            {
                if (e1 == e2) //if same or if both are null
                    return true;

                if (e1 == null || //if only 1 is null
                    e2 == null)
                    return false;

                if (String.CompareOrdinal(e1.Message, e2.Message) != 0)
                    return false;

                if (String.CompareOrdinal(e1.Source, e2.Source) != 0)
                    return false;

                if (String.CompareOrdinal(e1.StackTrace, e2.StackTrace) != 0)
                    return false;

                e1 = e1.InnerException;
                e2 = e2.InnerException;
            }
        }

        public static string ToDisplayString(this Exception e, bool displayInnerExceptions = true)
        {
            if (e == null)
                return null;

            StringBuilder errorMessage = new StringBuilder();
            errorMessage.Append(ExceptionToStringHelper(e));

            if (displayInnerExceptions)
            {
                if (e.InnerException != null)
                    errorMessage.Append(
                        InnerExceptionsToString(e.InnerException));

                ReflectionTypeLoadException reflectionTypeLoadException = e as ReflectionTypeLoadException;
                if (reflectionTypeLoadException != null &&
                    reflectionTypeLoadException.LoaderExceptions != null)
                {
                    for (int j = 0; j < reflectionTypeLoadException.LoaderExceptions.Length; j++)
                    {
                        errorMessage.Append(
                            Environment.NewLine +
                            Environment.NewLine +
                            "LoaderExceptions" + _leftBracket + j + _rightBracket +
                            Environment.NewLine +
                            _leftBracket +
                            Environment.NewLine);

                        errorMessage.Append(ExceptionToStringHelper(reflectionTypeLoadException.LoaderExceptions[j], _tab));

                        errorMessage.Append(InnerExceptionsToString(reflectionTypeLoadException.LoaderExceptions[j].InnerException, 1));

                        errorMessage.Append(
                            Environment.NewLine +
                            _rightBracket);
                    }
                }
            }

            return errorMessage.ToString();
        }

        private static string InnerExceptionsToString(Exception e, int tabs = 0, string initialBuffer = null)
        {
            if (e == null)
                return null;

            StringBuilder tabsStringBuilder = new StringBuilder();
            tabsStringBuilder.Append(initialBuffer);

            for (int i = tabs; i > 0; --i)
                tabsStringBuilder.Append(_tab);

            StringBuilder errorMessage = new StringBuilder();

            errorMessage.Append(
                Environment.NewLine +
                Environment.NewLine +
                tabsStringBuilder + "InnerException" + _leftBracket + tabs + _rightBracket +
                Environment.NewLine +
                tabsStringBuilder + _leftBracket +
                Environment.NewLine);

            errorMessage.Append(ExceptionToStringHelper(e, tabsStringBuilder + _tab));

            errorMessage.Append(InnerExceptionsToString(e.InnerException, tabs + 1, initialBuffer));

            errorMessage.Append(
                Environment.NewLine +
                tabsStringBuilder + _rightBracket);

            return errorMessage.ToString();
        }

        private static string ExceptionToStringHelper(Exception e, string lineBuffer = null)
        {
            if (e == null)
                return null;

            StringBuilder errorMessage = new StringBuilder();

            errorMessage.Append(lineBuffer + "Type: " + e.GetType().ToString());

            if (!String.IsNullOrEmpty(e.Message))
                errorMessage.Append(
                    Environment.NewLine +
                    Environment.NewLine +
                    lineBuffer + "Message: " + e.Message);

            if (!String.IsNullOrEmpty(e.Source))
                errorMessage.Append(
                    Environment.NewLine +
                    Environment.NewLine +
                    lineBuffer + "Source: " + e.Source);

            if (e.TargetSite != null)
            {
                string targetSite = e.TargetSite.ToString();

                if (!String.IsNullOrEmpty(targetSite))
                    errorMessage.Append(
                        Environment.NewLine +
                        Environment.NewLine +
                        lineBuffer + "TargetSite: " + targetSite);
            }

            if (!String.IsNullOrEmpty(e.StackTrace))
                errorMessage.Append(
                    Environment.NewLine +
                    Environment.NewLine +
                    lineBuffer + "StackTrace:" + Environment.NewLine +
                    e.StackTrace.IndentLines(lineBuffer));

            return errorMessage.ToString();
        }

        #endregion Members
    }
}